﻿Friend Class ScanFolder_GroupIndenticalFilenames
    Friend Shared Sub Scan(ByVal tracker As ScanTracker)
        If Not IO.Directory.Exists(tracker.CurrentPath) Then Return

        tracker.ValidPaths.Add(tracker.CurrentPath, Nothing)

        Dim aFileNames() As String = IO.Directory.GetFiles(tracker.CurrentPath)
        Dim aSubDirs() As String = IO.Directory.GetDirectories(tracker.CurrentPath)

        Dim aFoundGroups As Generic.Dictionary(Of String, Generic.List(Of String))
        aFoundGroups = GroupFiles(aFileNames)

        Dim aSharedItems As Generic.List(Of Entities.SharedItem)
        aSharedItems = DataStore.SharedItems.GetAllByDirectory(tracker.CurrentPath)

        Dim aSharedFiles As Generic.List(Of Entities.SharedItemFile)
        aSharedFiles = DataStore.SharedItemsFiles.GetAllByDirectory(tracker.CurrentPath)

        RemoveObsoleteSharedItemsAndFiles(tracker, aFileNames, aSharedItems, aSharedFiles)

        UpdateChangedExistingFiles(tracker, aSharedItems, aSharedFiles, aFileNames)

        AddMissingGroupsToDatabase(tracker, aSharedItems, aFoundGroups, aSharedFiles)

        AddNewFilesToExistingItems(tracker, aFileNames, aSharedItems, aSharedFiles)

        UpdateFileCountAndTotalSize(tracker, aSharedItems, aSharedFiles)

        For Each clsItem As Entities.SharedItem In tracker.ItemsThatChanged.Values
            Dim clsNewMediaInfo As Entities.MediaInfo = ScanFolder_Common.CreateMediaInfoEx(clsItem)
            If clsNewMediaInfo IsNot Nothing Then
                Using t As New DataStore.TransactionScope
                    DataStore.MediaInfo.Save(clsNewMediaInfo, t)
                    clsItem.HasMediaInfo = True
                    DataStore.SharedItems.Save(clsItem, t)
                    t.Complete()
                End Using
                Manager.OnSharedItemUpdated(clsItem)
            End If
        Next
        tracker.ItemsThatChanged.Clear()

        If tracker.Recursive Then
            For Each strSubDir As String In aSubDirs
                tracker.CurrentPath = strSubDir
                Scan(tracker)
            Next
        End If
    End Sub

    Private Shared Sub UpdateChangedExistingFiles(ByVal tracker As ScanTracker _
                                                 , ByVal sharedItems As Generic.List(Of Entities.SharedItem) _
                                                 , ByVal sharedFiles As Generic.List(Of Entities.SharedItemFile) _
                                                 , ByVal existingFiles() As String)

        Dim aChangedItems As New Generic.List(Of Entities.SharedItem)

        For Each strExistingFile As String In existingFiles
            For Each clsFile As Entities.SharedItemFile In sharedFiles
                If strExistingFile.ToLower = IO.Path.Combine(clsFile.Directory, clsFile.FileName).ToLower Then
                    Dim clsFI As New System.IO.FileInfo(strExistingFile)

                    Using t As New DataStore.TransactionScope
                        If ScanFolder_Common.UpdateSharedFileIfChanged(clsFI, clsFile, t) Then
                            For Each clsItem As Entities.SharedItem In sharedItems
                                If clsItem.Id = clsFile.SharedItemId Then
                                    aChangedItems.Add(clsItem)
                                    Exit For
                                End If
                            Next
                        End If
                        t.Complete()
                    End Using
                End If
            Next
        Next
        For Each clsItem As Entities.SharedItem In aChangedItems
            Dim clsMI As Entities.MediaInfo = ScanFolder_Common.CreateMediaInfoEx(clsItem)
            If clsMI IsNot Nothing Then
                Using t As New DataStore.TransactionScope
                    DataStore.MediaInfo.Save(clsMI, t)
                    clsItem.HasMediaInfo = True
                    DataStore.SharedItems.Save(clsItem, t)
                    t.Complete()
                End Using
                Manager.OnSharedItemUpdated(clsItem)
            End If
        Next
    End Sub

    Private Shared Sub UpdateFileCountAndTotalSize(ByVal tracker As ScanTracker _
                                                 , ByVal sharedItems As Generic.List(Of Entities.SharedItem) _
                                                 , ByVal sharedFiles As Generic.List(Of Entities.SharedItemFile))
        For Each clsItem As Entities.SharedItem In sharedItems
            Dim iFileCount As Short = 0
            Dim lTotalSize As Long = 0
            Dim dtLastChange As Date = DateTime.MinValue

            For Each clsFile As Entities.SharedItemFile In sharedFiles
                If clsFile.SharedItemId = clsItem.Id Then
                    iFileCount += CShort(1)
                    lTotalSize += clsFile.Size

                    If dtLastChange < clsFile.LastChanged Then dtLastChange = clsFile.LastChanged
                End If
            Next

            'if any of the properties have changed, update the item and save
            If clsItem.TotalFiles <> iFileCount _
               Or clsItem.TotalSize <> lTotalSize _
               Or clsItem.LastChanged <> dtLastChange Then
                clsItem.TotalSize = lTotalSize
                clsItem.TotalFiles = iFileCount
                clsItem.LastChanged = dtLastChange

                Dim clsMI As Entities.MediaInfo = ScanFolder_Common.CreateMediaInfoEx(clsItem)

                Using t As New DataStore.TransactionScope
                    If clsMI IsNot Nothing Then
                        clsItem.HasMediaInfo = True
                        clsMI.SharedItemId = clsItem.Id
                        DataStore.MediaInfo.Save(clsMI, t)
                    End If
                    DataStore.SharedItems.Save(clsItem, t)
                    t.Complete()
                End Using
                Manager.OnSharedItemUpdated(clsItem)
            End If
        Next
    End Sub

    Private Shared Sub AddNewFilesToExistingItems(ByVal tracker As ScanTracker _
                                               , ByVal existingFiles() As String _
                                               , ByVal sharedItems As Generic.List(Of Entities.SharedItem) _
                                               , ByVal sharedFiles As Generic.List(Of Entities.SharedItemFile))

        For Each strFile As String In existingFiles
            Dim blnExists As Boolean = False
            For Each clsFile As Entities.SharedItemFile In sharedFiles
                If IO.Path.GetFileName(strFile).ToLower = clsFile.FileName.ToLower Then
                    blnExists = True
                    Exit For
                End If
            Next

            If Not blnExists Then
                Dim strName As String = IO.Path.GetFileNameWithoutExtension(strFile).ToLower
                'find item to add it to
                Dim clsItem As Entities.SharedItem = Nothing
                For Each clsTmpItem As Entities.SharedItem In sharedItems
                    If clsTmpItem.Name.ToLower = strName Then
                        clsItem = clsTmpItem
                        Exit For
                    End If
                Next
                If clsItem Is Nothing Then
                    strName = IO.Path.GetFileNameWithoutExtension(strName)
                    For Each clsTmpItem As Entities.SharedItem In sharedItems
                        If clsTmpItem.Name.ToLower = strName Then
                            clsItem = clsTmpItem
                            Exit For
                        End If
                    Next
                End If
                If clsItem IsNot Nothing Then
                    Dim clsFile As Entities.SharedItemFile = ScanFolder_Common.CreateSharedItemFile(strFile)
                    clsFile.SharedItemId = clsItem.Id
                    DataStore.SharedItemsFiles.Save(clsFile)
                    sharedFiles.Add(clsFile)

                    tracker.AddChangedItem(clsItem)
                End If
            End If
        Next
    End Sub

    Private Shared Sub RemoveObsoleteSharedItemsAndFiles(ByVal tracker As ScanTracker _
                                               , ByVal existingFiles() As String _
                                               , ByVal sharedItems As Generic.List(Of Entities.SharedItem) _
                                               , ByVal sharedFiles As Generic.List(Of Entities.SharedItemFile))

        'first, delete SharedItemFiles that no longer exist on disk
        For i As Integer = sharedFiles.Count - 1 To 0 Step -1
            Dim clsSharedFile As Entities.SharedItemFile = sharedFiles(i)

            Dim blnObsolete As Boolean = True
            For Each strFilename As String In existingFiles
                If IO.Path.GetFileName(strFilename.ToLower) = clsSharedFile.FileName.ToLower Then
                    blnObsolete = False
                    Exit For
                End If
            Next

            If blnObsolete Then
                Using t As New DataStore.TransactionScope
                    DataStore.SharedItemsFiles.SetFileAsOrphan(clsSharedFile, t)
                    t.Complete()
                End Using
                sharedFiles.RemoveAt(i)
                Manager.OnLogEvent(String.Format("Removed obsolete file ""{0}"" from database", clsSharedFile.FileName))
                For Each clsItem As Entities.SharedItem In sharedItems
                    If clsItem.Id = clsSharedFile.SharedItemId Then
                        tracker.AddChangedItem(clsItem)
                        Exit For
                    End If
                Next
            End If
        Next

        'now, remove SharedItems that no longer have files in them
        For i As Integer = sharedItems.Count - 1 To 0 Step -1
            Dim clsItem As Entities.SharedItem = sharedItems(i)
            Dim blnIsEmpty As Boolean = True
            For Each clsFile As Entities.SharedItemFile In sharedFiles
                If clsFile.SharedItemId = clsItem.Id Then
                    blnIsEmpty = False
                    Exit For
                End If
            Next

            If blnIsEmpty Then
                tracker.RemoveChangedItem(clsItem)
                Using t As New DataStore.TransactionScope
                    DataStore.SharedItems.Delete(clsItem, t)
                    t.Complete()
                End Using
                sharedItems.RemoveAt(i)
                Manager.OnSharedItemDeleted(clsItem)
                Manager.OnLogEvent(String.Format("Removed empty item ""{0}"" from database", clsItem.Name))
            End If
        Next
    End Sub

    Private Shared Sub AddMissingGroupsToDatabase(ByVal tracker As ScanTracker _
                                                , ByVal sharedItems As Generic.List(Of Entities.SharedItem) _
                                                , ByVal groups As Generic.Dictionary(Of String, Generic.List(Of String)) _
                                                , ByVal sharedFiles As Generic.List(Of Entities.SharedItemFile))

        For Each strKey As String In groups.Keys
            Dim aGroupFiles As Generic.List(Of String) = groups.Item(strKey)
            Dim clsItem As Entities.SharedItem
            clsItem = FindItemByName(strKey, sharedItems)
            If clsItem Is Nothing Then
                Dim aItemFiles As Generic.List(Of Entities.SharedItemFile)
                aItemFiles = CreateSharedItemFiles(aGroupFiles)

                Dim clsSharedItem As New Entities.SharedItem
                clsSharedItem.Added = Now.ToUniversalTime
                clsSharedItem.LastChanged = GetLastChanged(aItemFiles)
                clsSharedItem.Directory = IO.Path.GetDirectoryName(aGroupFiles(0))
                clsSharedItem.Name = IO.Path.GetFileNameWithoutExtension(aGroupFiles(0))
                clsSharedItem.SourceWatchFolderId = tracker.Share.Id
                clsSharedItem.GroupMode = tracker.Share.GroupMode
                clsSharedItem.ItemType = tracker.Share.ItemTypes
                clsSharedItem.TotalFiles = CShort(aItemFiles.Count)
                clsSharedItem.TotalSize = GetTotalSize(aItemFiles)

                clsSharedItem.UniqueId = Utils.Misc.GenerateUniqueId

                Dim clsMI As Entities.MediaInfo = ScanFolder_Common.CreateMediaInfoEx(clsSharedItem, aItemFiles)

                Using t As New DataStore.TransactionScope
                    DataStore.SharedItems.Save(clsSharedItem, t)

                    For Each clsFileItem As Entities.SharedItemFile In aItemFiles
                        clsFileItem.SharedItemId = clsSharedItem.Id
                        DataStore.SharedItemsFiles.Save(clsFileItem, t)
                        sharedFiles.Add(clsFileItem)
                    Next

                    If clsMI IsNot Nothing Then
                        clsMI.SharedItemId = clsSharedItem.Id
                        DataStore.MediaInfo.Save(clsMI, t)
                        clsSharedItem.HasMediaInfo = True
                        DataStore.SharedItems.Save(clsSharedItem, t)
                    End If

                    t.Complete()
                End Using

                sharedItems.Add(clsSharedItem)

                Manager.OnSharedItemCreated(clsSharedItem)
                Manager.OnLogEvent(String.Format("Added new shared item ""{0}""", clsSharedItem.Name))
            End If
        Next
    End Sub
    Private Shared Function CreateSharedItemFiles(ByVal fileNames As Generic.List(Of String)) As Generic.List(Of Entities.SharedItemFile)
        Dim aRet As New Generic.List(Of Entities.SharedItemFile)
        For Each strFilePath As String In fileNames
            Dim clsFile As Entities.SharedItemFile = ScanFolder_Common.CreateSharedItemFile(strFilePath)
            aRet.Add(clsFile)
        Next
        Return aRet
    End Function

    Private Shared Function GetLastChanged(ByVal itemFiles As Generic.List(Of Entities.SharedItemFile)) As Date
        Dim dtRet As Date = Date.MinValue
        For Each clsItemFile As Entities.SharedItemFile In itemFiles
            If clsItemFile.LastChanged > dtRet Then dtRet = clsItemFile.LastChanged
        Next
        Return dtRet
    End Function
    Private Shared Function GetTotalSize(ByVal itemFiles As Generic.List(Of Entities.SharedItemFile)) As Long
        Dim lRet As Long = 0
        For Each clsItemFile As Entities.SharedItemFile In itemFiles
            lRet += clsItemFile.Size
        Next
        Return lRet
    End Function

    Private Shared Function FindItemByName(ByVal name As String, ByVal items As Generic.List(Of Entities.SharedItem)) As Entities.SharedItem
        For Each clsItem As Entities.SharedItem In items
            If clsItem.Name.ToLower = name Then Return clsItem
        Next
        Return Nothing
    End Function
    Private Shared Function FindFilesBySharedItem(ByVal item As Entities.SharedItem, ByVal files As Generic.List(Of Entities.SharedItemFile)) As Generic.List(Of Entities.SharedItemFile)
        Dim aRet As New Generic.List(Of Entities.SharedItemFile)
        For Each clsFile As Entities.SharedItemFile In files
            If clsFile.SharedItemId = item.Id Then aRet.Add(clsFile)
        Next
        Return aRet
    End Function

    Private Shared Function GroupFiles(ByVal files() As String) As Generic.Dictionary(Of String, Generic.List(Of String))
        Dim aGroups As New Generic.Dictionary(Of String, Generic.List(Of String))
        For Each strFileName As String In files
            If Not strFileName.ToLower.EndsWith(".srt") Then
                Dim strKey As String = IO.Path.GetFileNameWithoutExtension(strFileName).ToLower
                If aGroups.ContainsKey(strKey) Then
                    aGroups(strKey).Add(strFileName)
                Else
                    Dim aList As New Generic.List(Of String)
                    aList.Add(strFileName)
                    aGroups.Add(strKey, aList)
                End If
            End If
        Next
        For Each strFileName As String In files
            If strFileName.ToLower.EndsWith(".srt") Then
                Dim strKey As String = IO.Path.GetFileNameWithoutExtension(strFileName).ToLower
                If aGroups.ContainsKey(strKey) Then
                    aGroups(strKey).Add(strFileName)
                Else
                    strKey = IO.Path.GetFileNameWithoutExtension(strKey)
                    If aGroups.ContainsKey(strKey) Then
                        aGroups(strKey).Add(strFileName)
                    End If
                End If
            End If
        Next

        Return aGroups
    End Function

End Class
